package com.huawei.util;

import lombok.extern.slf4j.Slf4j;

import javax.annotation.PreDestroy;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author wujinpeng
 * @version 1.0
 * @date 2025/2/2 16:49
 * @description 命令执行器工具类
 */
@Slf4j
public class CommandExecutor {

    private static final ExecutorService streamExecutor = Executors.newCachedThreadPool();

    public static class CommandResult {
        private final int exitCode;
        private final String stdout;
        private final String stderr;

        public CommandResult(int exitCode, String stdout, String stderr) {
            this.exitCode = exitCode;
            this.stdout = stdout;
            this.stderr = stderr;
        }

        // Getters
        public int getExitCode() { return exitCode; }
        public String getStdout() { return stdout; }
        public String getStderr() { return stderr; }
    }

    public static CommandResult execute(String command, String workingDirectory, long timeout, TimeUnit timeUnit)
            throws IOException, InterruptedException, TimeoutException {
        return execute(command, workingDirectory, timeout, timeUnit, getSystemCharset());
    }

    public static CommandResult execute(String command, String workingDirectory, long timeout, TimeUnit timeUnit, Charset charset)
            throws IOException, InterruptedException, TimeoutException {
        // 验证工作目录
        File workDir = validateWorkingDirectory(workingDirectory);

        // 构建进程命令
        ProcessBuilder processBuilder = buildProcess(command);
        processBuilder.directory(workDir);
        processBuilder.redirectErrorStream(false); // 分别获取stdout和stderr

        // 启动进程
        Process process = processBuilder.start();

        try {
            // 异步读取输出流
            Future<String> stdoutFuture = readStreamAsync(process.getInputStream(), charset);
            Future<String> stderrFuture = readStreamAsync(process.getErrorStream(), charset);

            // 等待进程终止
            if (!process.waitFor(timeout, timeUnit)) {
                throw new TimeoutException("命令执行超时");
            }

            // 获取输出结果
            String stdout = stdoutFuture.get(timeout, timeUnit);
            String stderr = stderrFuture.get(timeout, timeUnit);

            return new CommandResult(process.exitValue(), stdout, stderr);
        } catch (ExecutionException e) {
            throw new IOException("流读取失败", e);
        } finally {
            // 确保进程终止
            if (process.isAlive()) {
                process.destroyForcibly();
            }
        }
    }

    private static ProcessBuilder buildProcess(String command) {
        String[] cmdArray;
        if (isWindows()) {
            cmdArray = new String[]{"cmd.exe", "/c", command};
        } else {
            cmdArray = new String[]{"/bin/sh", "-c", command};
        }
        return new ProcessBuilder(cmdArray);
    }

    private static File validateWorkingDirectory(String workingDirectory) {
        if (workingDirectory == null || workingDirectory.trim().isEmpty()) {
            return new File(System.getProperty("user.dir"));
        }

        File workDir = new File(workingDirectory);
        if (!workDir.exists() || !workDir.isDirectory()) {
            throw new IllegalArgumentException("无效的工作目录: " + workingDirectory);
        }
        return workDir;
    }

    private static Future<String> readStreamAsync(InputStream inputStream, Charset charset) {
        return streamExecutor.submit(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset))) {
                StringBuilder output = new StringBuilder();
                char[] buffer = new char[1024];
                int bytesRead;
                while ((bytesRead = reader.read(buffer)) != -1) {
                    output.append(buffer, 0, bytesRead);
                }
                return output.toString().trim();
            }
        });
    }

    private static Charset getSystemCharset() {
        return isWindows() ? Charset.forName("GBK") : StandardCharsets.UTF_8;
    }

    private static boolean isWindows() {
        return System.getProperty("os.name").toLowerCase().contains("win");
    }

    // 关闭线程池的方法
    public static void shutdown() {
        if (streamExecutor != null && !streamExecutor.isShutdown()) {
            streamExecutor.shutdown(); // 停止接受新任务
            try {
                // 等待现有任务完成
                if (!streamExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
                    streamExecutor.shutdownNow(); // 强制终止任务
                }
            } catch (InterruptedException e) {
                log.error("线程池关闭时被中断", e);
                streamExecutor.shutdownNow(); // 强制终止任务
                Thread.currentThread().interrupt(); // 恢复中断状态
            }
        }
    }

    // 在 Bean 销毁时关闭线程池
    @PreDestroy
    public void destroy() {
        shutdown();
    }

    // 使用示例
    public static void main(String[] args) {
        try {
//            CommandResult result = execute("echo Hello World && dir", "D:\\AAA-学习笔记", 10, TimeUnit.SECONDS);
            CommandResult result = execute("echo Hello World && dir", null, 10, TimeUnit.SECONDS);
            System.out.println("Exit Code: " + result.getExitCode());
            System.out.println("STDOUT:\n" + result.getStdout());
            System.out.println("STDERR:\n" + result.getStderr());
        } catch (Exception e) {
            log.error("命令执行失败", e);
        }
    }
}
